package com.ssy.lingxi.settle.accounts.serviceimpl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.order.OrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.dto.request.*;
import com.ssy.lingxi.dto.response.PlatformCouponSettlementDetailResponseVO;
import com.ssy.lingxi.dto.response.PlatformCouponSettlementResponseVO;
import com.ssy.lingxi.marketing.api.model.request.CouponQueryReq;
import com.ssy.lingxi.marketing.api.model.response.CouponInfoResp;
import com.ssy.lingxi.message.api.constant.MessageTemplateCode;
import com.ssy.lingxi.message.api.vo.request.SystemMessageRequest;
import com.ssy.lingxi.pay.api.dto.request.eAccount.PlatFormTransferRequest;
import com.ssy.lingxi.pay.api.model.vo.response.allInPay.ApplicationTransferRespVO;
import com.ssy.lingxi.report.api.enums.OperateDataSourceEnum;
import com.ssy.lingxi.report.api.enums.SettleAccountPlatformScoreOperateTypeEnum;
import com.ssy.lingxi.report.api.vo.request.OperateNoticeVO;
import com.ssy.lingxi.settle.accounts.entity.PlatformCouponSettlementDO;
import com.ssy.lingxi.settle.accounts.entity.PlatformCouponSettlementDetailDO;
import com.ssy.lingxi.settle.accounts.entity.PlatformSettlementStrategyDO;
import com.ssy.lingxi.settle.accounts.entity.PlatformSettlementStrategyMemberDO;
import com.ssy.lingxi.settle.accounts.model.bo.PayProveBO;
import com.ssy.lingxi.settle.accounts.model.constant.*;
import com.ssy.lingxi.settle.accounts.model.vo.request.AllInPayVO;
import com.ssy.lingxi.settle.accounts.model.vo.request.ConfirmAccountCompleteVO;
import com.ssy.lingxi.settle.accounts.model.vo.request.ConfirmPayProve;
import com.ssy.lingxi.settle.accounts.model.vo.request.PayVO;
import com.ssy.lingxi.settle.accounts.repository.PlatformCouponSettlementDetailRepository;
import com.ssy.lingxi.settle.accounts.repository.PlatformCouponSettlementRepository;
import com.ssy.lingxi.settle.accounts.repository.PlatformSettlementStrategyRepository;
import com.ssy.lingxi.settle.accounts.service.IFeignService;
import com.ssy.lingxi.settle.accounts.service.IPlatformCouponSettlementService;
import com.ssy.lingxi.settle.accounts.utils.OrderDateTimeUtil;
import com.ssy.lingxi.settle.accounts.utils.SerialNumberUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 平台优惠券结算
 *
 * @author 彭冬冬
 * @version 2.0.0
 * @date 2022/1/17 16:55
 */
@Service
public class PlatformCouponSettlementServiceImpl implements IPlatformCouponSettlementService {
    private final static Logger logger = LoggerFactory.getLogger(PlatformCouponSettlementServiceImpl.class);

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private PlatformCouponSettlementRepository repository;

    @Resource
    private PlatformCouponSettlementDetailRepository detailRepository;

    @Resource
    private PlatformSettlementStrategyRepository platformSettlementStrategyRepository;

    @Resource
    private IFeignService feignService;

    @Override
    public Wrapper<Void> save(String message) {
        logger.info("接收到MQ待对账单订单商品信息 =>" +message);
        PlatformCouponSettlementRequestVO requestVO = SerialNumberUtils.deserializeObject(message, PlatformCouponSettlementRequestVO.class);
        if (requestVO == null) {
            logger.error("平台优惠券结算:" + message);
            return Wrapper.fail(ResponseCode.PLATFORM_COUPON_SETTLEMENT_SAVE_ERROR);
        }
        return save(requestVO);
    }


    @Override
    public Wrapper<Void> save(PlatformCouponSettlementRequestVO requestVO) {
        // step1: 组装数据
        List<PlatformCouponSettlementDetailRequestVO> details = requestVO.getDetails();
        PlatformCouponSettlementDO entity = new PlatformCouponSettlementDO();
        // entity.setId(0L); // id
        entity.setMemberId(requestVO.getMemberId()); // 会员id
        entity.setRoleId(requestVO.getRoleId()); // 角色id
        entity.setSettlementName(requestVO.getSettlementName()); // 结算名称
//        String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
//        entity.setSettlementNo(settlementNo); // 结算流水号
        entity.setSettlementDate(0L); // 结算日期
        // entity.setSettlementWay(0); // 结算方式
        // entity.setStartTime(0L); // 开始时间
        // entity.setEndTime(0L); // 结束时间
        entity.setTotalCount((long) details.size()); // 明细总数
        Double amount = details.stream().map(PlatformCouponSettlementDetailRequestVO::getCouponAmount).map(BigDecimal::valueOf).reduce(BigDecimal::add).map(BigDecimal::doubleValue).orElse(0.0D);
        entity.setAmount(amount); // 结算金额
        entity.setSettlementTime(0L); // 结算时间
        entity.setEstimatedPaymentDate(0L); // 预计付款日期
        // entity.setActualPaymentTime(0L); // 实际付款日期
        //entity.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
        // entity.setPayWay(0); // 支付方式
        // entity.setStrategyId(0L); // 策略id
        // entity.setUpdateTime(0L); // 更新时间
        long payTime = System.currentTimeMillis();
        entity.setCreateTime(payTime);
        // entity.setPayProveList(Lists.newArrayList());
        // 查询优化券
        List<Long> couponIds = details.stream().map(PlatformCouponSettlementDetailRequestVO::getCouponId).collect(Collectors.toList());
        List<CouponInfoResp> couponList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(couponIds)) {
            CouponQueryReq request = new CouponQueryReq();
            request.setBelongType(1);
            request.setCouponIds(couponIds);
            Wrapper<List<CouponInfoResp>> couponData = feignService.getCouponDetailListByIds(request);
            couponList.addAll(couponData.getData());
        }
        // 退货单查询明细，是否重复
        Set<String> returnCodeSet = details.stream().map(PlatformCouponSettlementDetailRequestVO::getReturnCode).filter(Objects::nonNull).collect(Collectors.toSet());
        Set<Long> returnDetailIdSet = details.stream().filter(f -> Objects.nonNull(f.getReturnDetailId()) && f.getReturnDetailId().compareTo(0L) > 0).map(PlatformCouponSettlementDetailRequestVO::getReturnDetailId).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(returnCodeSet)
                && !CollectionUtils.isEmpty(returnDetailIdSet)) {
            Specification<PlatformCouponSettlementDetailDO> spec = (root, query, builder) -> {
                CriteriaBuilder.In<String> returnCode = builder.in(root.get("returnCode").as(String.class));
                returnCodeSet.forEach(returnCode::value);
                CriteriaBuilder.In<Long> returnDetailId = builder.in(root.get("returnDetailId").as(Long.class));
                returnDetailIdSet.forEach(returnDetailId::value);
                return query.where(returnCode, returnDetailId).getRestriction();
            };
            List<PlatformCouponSettlementDetailDO> oldDetailEntities = detailRepository.findAll(spec);
            details = details.stream().filter(f -> oldDetailEntities.stream().noneMatch(f2 ->
                    Objects.equals(f.getReturnCode(), f2.getReturnCode())
                            &&  Objects.equals(f.getReturnDetailId(), f2.getReturnDetailId()))).collect(Collectors.toList());
        }
        List<PlatformCouponSettlementDetailDO> detailEntities = details.stream().map(map -> {
            PlatformCouponSettlementDetailDO item = new PlatformCouponSettlementDetailDO();
            item.setMemberId(requestVO.getMemberId());
            item.setRoleId(requestVO.getRoleId());
            item.setSettlement(entity); // 平台优惠券结算
            item.setOrderNo(map.getOrderNo()); // 单据号
            item.setOrderAbstract(map.getOrderAbstract()); // 单据摘要
            item.setSettlementOrderType(map.getSettlementOrderType()); // 单据类型
            item.setReturnCode(map.getReturnCode()); // 退货申请单号
            item.setOrderTime(map.getOrderTime()); // 单据时间
            item.setOrderType(map.getOrderType()); // 订单类型
            item.setOrderAmount(map.getOrderAmount()); // 单据总额
            item.setCouponId(map.getCouponId()); // 优惠券id
            String couponNo = couponList.stream().filter(f -> Objects.equals(f.getId(), map.getCouponId())).findFirst().map(CouponInfoResp::getCode).orElse("");
            item.setCouponNo(couponNo); // 优惠券编码
            item.setCouponAmount(map.getCouponAmount()); // 优惠券金额
            item.setPayTime(payTime); // 支付时间
            item.setSettlementAmount(map.getCouponAmount()); // 结算金额
            return item;
        }).collect(Collectors.toList());
        entity.setDetails(detailEntities);
        repository.saveAndFlush(entity);
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> timingSettlement(LocalDateTime startDate) {
        logger.info("开始统计平台结算-优惠券结算");
        // step1: 获取平台有效结算策略
        List<PlatformSettlementStrategyDO> strategyList = findPlatformSettlementStrategyList(startDate);
        if (strategyList.size() <= 0) {
            logger.info("统计平台结算-优惠券结算:当前没有符合需要结算的策略");
            return Wrapper.success();
        }
        // step2: 处理结算
        for (PlatformSettlementStrategyDO strategy:
        strategyList) {
            logger.info("统计平台结算-优惠券结算:start:{}-{}", strategy.getId(), strategy.getName());
            // 通用处理
            platformCouponSettlementForStrategy(strategy, startDate);
            logger.info("统计平台结算-优惠券结算:end:{}-{}", strategy.getId(), strategy.getName());
        }

        return Wrapper.success();
    }

    /**
     * 根据结算策略处理平台优惠券策略
     * @param strategy 平台结算策略
     * @param startDate 开始时间
     */
    @Transactional(rollbackFor = Exception.class)
    public void platformCouponSettlementForStrategy(PlatformSettlementStrategyDO strategy, LocalDateTime startDate) {
        // step1: 入参校验
        if (Objects.isNull(strategy)) {
            logger.info("统计平台结算-优惠券结算:结算策略为空");
            return;
        }
        // step2: 获取策略关联会员列表
        List<PlatformSettlementStrategyMemberDO> memberList = new ArrayList<>();
        // 是否默认全部会员
        if (Objects.equals(CommonBooleanEnum.YES.getCode(), strategy.getIsDefault())) {
            //获得上个月的第一天
            long startTime = startDate.plusMonths(-1).withDayOfMonth(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            long endTime = startDate.withHour(23).withMinute(59).withSecond(59).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            memberList.addAll(getSettleMemberListByTime(startTime, endTime));

        } else {
            memberList.addAll(findStrategyDefaultMemberList(strategy));
        }
        if(CollectionUtil.isEmpty(memberList)){
            logger.info("统计平台结算-优惠券结算:结算策略[{}-{}]未有会员或不存在订单", strategy.getId(), strategy.getName());
            return;
        }
        // step3: 遍历会员结算
        for (PlatformSettlementStrategyMemberDO member:
        memberList) {
            Long memberId = member.getMemberId();
            Long roleId = member.getRoleId();
            try {
                Wrapper<Void> wrapper = platformCouponSettlementCommon(strategy, startDate, memberId, roleId);
                if (wrapper == null || wrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                    logger.info("统计平台结算-代收账款:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategy.getId(), strategy.getName(), memberId, roleId);
                }
            } catch (Exception e) {
                logger.error("统计平台结算-代收账款:结算策略[{}-{}-结算会员:{}-结算角色:{}],结算失败", strategy.getId(), strategy.getName(), memberId, roleId,e);
            }
        }

    }

    private List<PlatformSettlementStrategyMemberDO> getSettleMemberListByTime(long startTime, long endTime) {
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            Predicate settlementDate = builder.equal(root.get("settlementDate").as(Long.class), 0L);
            Predicate createTimeStart = builder.ge(root.get("createTime").as(Long.class), startTime);
            Predicate createTimeEnd = builder.le(root.get("createTime").as(Long.class), endTime);
            return query.where(settlementDate, createTimeStart, createTimeEnd).getRestriction();
        };
        List<PlatformCouponSettlementDO> list = repository.findAll(spec);
        return list.stream().map(map -> {
            PlatformSettlementStrategyMemberDO item = new PlatformSettlementStrategyMemberDO();
            item.setMemberId(map.getMemberId());
            item.setRoleId(map.getRoleId());
            return item;
        }).collect(Collectors.toList());
    }

    /**
     * 通用结算方法
     * @param strategy 结算策略
     * @param startDate 开始结算时间
     * @param memberId 结算会员id
     * @param roleId 结算角色id
     *
     * @return 结算结果
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public Wrapper<Void> platformCouponSettlementCommon(PlatformSettlementStrategyDO strategy, LocalDateTime startDate, Long memberId, Long roleId/*, List<PlatformCouponSettlementDetailDO> oldDetailEntities*/) {
        // step1: 获取平台优惠券结算信息
        long settlementDate = startDate.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        PlatformCouponSettlementDO entity = getBySettlementDateAndMemberIdAndRoleIdAndSettlementWay(settlementDate, memberId, roleId, strategy.getSettlementWay());
        // step2: 旧数据存在，提取对应历史的明细项
        // 账期方式
        SettlementWayEnum settlementWay = SettlementWayEnum.getEnum(strategy.getSettlementWay());
        if (entity != null) {
            if (!entity.getStatus().equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode())) {
                logger.info("统计平台结算-优惠券:结算策略[{}-{}-结算会员:{}-结算角色:{},已结算", strategy.getId(), strategy.getName(), memberId, roleId);
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
            }
            // 提取对应历史的明细项
            List<PlatformCouponSettlementDetailDO> detailEntities = new ArrayList<>(entity.getDetails());
            entity.setDetails(detailEntities);
        }
        // step3: 旧数据不存在，合并同会员、同角色，结算日期为0
        else {
            // 查询会员、同角色，结算日期为0的平台优惠券结算
            List<PlatformCouponSettlementDO> entities = findPendingSettlementByMemberIdAndRoleId(memberId, roleId);
            if (CollectionUtils.isEmpty(entities)) {
                return Wrapper.success();
            }
            // 选取保存一项平台优惠券结算
            // 交易订单号
            String settlementNo = SerialNumberUtils.getNumber(redisStringUtils, Constants.SETTLEMENT_CODE, Constants.SETTLEMENT_CODE_DATE, Constants.SETTLEMENT_CODE_NUM_LEN);
            // 账期处理：更新结算时间和结算日期
            if (settlementWay == SettlementWayEnum.ACCOUNT_PERIOD) {
                // 获取平台结算策略结算日
                Integer settlementDays = strategy.getSettlementDays();
                // 更新设置查询所有结算日期时间戳是0L的，结算日期、结算时间、预付款日期、实际付款日期
                for (PlatformCouponSettlementDO one:
                entities) {
                    LocalDateTime createDateTime = OrderDateTimeUtil.timestampToLocalDateTime(one.getCreateTime());
                    LocalDateTime settlementDateTime = createDateTime.plusDays(settlementDays).withHour(1).withMinute(0).withSecond(0);
                    // 结算日期
                    long targetSettlementDate = settlementDateTime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    logger.info("结算日期{}", targetSettlementDate);
                    one.setSettlementDate(targetSettlementDate);
                    // 结算时间
                    long targetSettlementDateTime = settlementDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    logger.info("结算时间{}", targetSettlementDateTime);
                    one.setSettlementTime(targetSettlementDateTime);
                    // 预计付款日期
                    // 如果结算日期小于当月付款日期，则取当月付款日期，如大于则取下月付款日期，如未设置付款日期，则等于结算日期
                    long estimatedPaymentDate = Optional.ofNullable(strategy.getEstimatedPaymentDate()).map(map -> {
                        LocalDateTime estimatedPaymentDatetime = settlementDateTime.withDayOfMonth(map);
                        return estimatedPaymentDatetime.compareTo(settlementDateTime) >= 0
                                ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                                : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    }).orElse(targetSettlementDate);
                    one.setEstimatedPaymentDate(estimatedPaymentDate); // 预计付款日期
                    // 实际付款日期
                    one.setActualPaymentTime(0L); // 实际付款日期
                }
            }
            // 月结处理
            else if (settlementWay == SettlementWayEnum.MONTH_SETTLEMENT) {
                // 获取平台结算策略结算日
                Integer settlementDays = strategy.getSettlementDate();

                // 获取结算日期的当前天
                int dayOfMonth = startDate.getDayOfMonth();
                if (!Objects.equals(settlementDays, dayOfMonth)) {
                    // 平台结算策略结算日 != 结算日期的当前天，这不用结算
                    return Wrapper.success();
                }
                // 更新设置查询所有结算日期时间戳是0L的，结算日期、结算时间、预付款日期、实际付款日期
                for (PlatformCouponSettlementDO one:
                     entities) {
                    LocalDateTime createDateTime = OrderDateTimeUtil.timestampToLocalDateTime(one.getCreateTime());
                    LocalDateTime monthSettlementDateTime = createDateTime.plusMonths(1).withHour(1).withMinute(0).withSecond(0);
                    // 结算日期
                    long monthTargetSettlementDate = monthSettlementDateTime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    one.setSettlementDate(monthTargetSettlementDate);
                    // 结算时间
                    long monthTargetSettlementDateTime = monthSettlementDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    one.setSettlementTime(monthTargetSettlementDateTime);
                    // 预计付款日期：如果结算日期小于当月付款日期，则取当月付款日期，如大于则取下月付款日期，如未设置付款日期，则等于结算日期
                    long estimatedPaymentDate = Optional.ofNullable(strategy.getEstimatedPaymentDate()).map(map -> {
                        LocalDateTime estimatedPaymentDatetime = monthSettlementDateTime.withDayOfMonth(map);
                        return estimatedPaymentDatetime.compareTo(monthSettlementDateTime) >= 0
                                ? estimatedPaymentDatetime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
                                : estimatedPaymentDatetime.plusMonths(1).toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                    }).orElse(monthTargetSettlementDate);
                    one.setEstimatedPaymentDate(estimatedPaymentDate); // 预计付款日期
                    one.setActualPaymentTime(0L); // 实际付款日期
                }
            } else {
                logger.info("统计平台优惠券结算-生成通知单:结算策略[{}-{}]结算方式有误", strategy.getId(), strategy.getName());
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
            }
            // 过滤获取结算日期符合当前的平台优惠券结算数据
            List<PlatformCouponSettlementDO> targetEntities = entities.stream().filter(f -> Objects.equals(f.getSettlementDate(), settlementDate)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(targetEntities)) {
                // 没有符合条件的数据不用执行
                return Wrapper.success();
            }
            // 汇总所有明细项
            List<PlatformCouponSettlementDetailDO> allDetailEntities = targetEntities.stream().flatMap(map -> map.getDetails().stream()).collect(Collectors.toList());
//            allDetailEntities.addAll(oldDetailEntities);
            entity = targetEntities.get(0);
            PlatformCouponSettlementDO finalEntity = entity;
            allDetailEntities = new ArrayList<>(allDetailEntities.stream().collect(Collectors.toMap(PlatformCouponSettlementDetailDO::getId, value -> value, (existingValue, newValue) -> newValue)).values());
            allDetailEntities.forEach(map -> map.setSettlement(finalEntity));
            entity.setSettlementNo(settlementNo); // 结算流水号
            entity.setSettlementWay(strategy.getSettlementWay()); // 结算方式
            entity.setTotalCount((long) allDetailEntities.size());// 明细总数
            Double amount = allDetailEntities.stream().map(PlatformCouponSettlementDetailDO::getSettlementAmount).map(BigDecimal::valueOf).reduce(BigDecimal::add).map(BigDecimal::doubleValue).orElse(0.0D);
            entity.setAmount(amount);// 结算金额

            entity.setStatus(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode());
            entity.setPayWay(strategy.getSettlementPaymentType()); // 支付方式
            entity.setStrategyId(strategy.getId()); // 策略id
            entity.setUpdateTime(0L); // 更新时间
            entity.setDetails(allDetailEntities);
            repository.saveAndFlush(entity);
            // 删除符合结算日期多余的实体
            List<PlatformCouponSettlementDO> removeEntities = targetEntities.stream().filter(f -> !f.getId().equals(finalEntity.getId())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(removeEntities)) {
                repository.deleteAll(removeEntities);
            }
        }
        // step4: 更新执行结算开始时间和结算时间
        long endTime;
        switch (settlementWay) {
            case ACCOUNT_PERIOD:
                // 账期结算
                // 账期结算截止时间:T+账期天数 (结算) （结算日期）
                Integer settlementDays = strategy.getSettlementDays();
                endTime = startDate.plusDays(-settlementDays).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                break;
            case MONTH_SETTLEMENT:
                // 账期月结
                endTime = startDate.plusMonths(-1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                break;
            default:
                logger.info("统计平台结算-生成通知单:结算策略[{}-{}]结算方式有误", strategy.getId(), strategy.getName());
                throw new BusinessException(ResponseCode.SA_SETTLEMENT_WAY_ERROR);
        }
        // 获取最后一次优惠券结算
        PlatformCouponSettlementDetailDO lastDetailEntity = getDetailByMemberIdAndRoleIdOrderByPayTimeDescFirst(memberId, roleId);
        // 在原有时间上加1，防止后续查询数据重复
        long startTime = Optional.ofNullable(lastDetailEntity).map(map -> map.getPayTime() + 1000).orElse(0L);
        // 设置结算时间范围
        entity.setStartTime(startTime); // 开始时间
        entity.setEndTime(endTime); // 结束时间
        // 查询定时操作时，有没有多出的数据
        List<PlatformCouponSettlementDO> newEntities =  findByMemberIdAndRoleIdAndEndTimeAndStartTime(memberId, roleId, endTime, startTime, strategy, settlementDate);
        List<PlatformCouponSettlementDetailDO> newDetailEntities = newEntities.stream().flatMap(map -> map.getDetails().stream()).collect(Collectors.toList());
        if (!CollectionUtil.isEmpty(newDetailEntities)) {
            for (PlatformCouponSettlementDetailDO detailEntity:
            newDetailEntities) {
                detailEntity.setSettlement(entity);
            }
            List<PlatformCouponSettlementDetailDO> detailEntities = entity.getDetails();
            detailEntities.addAll(newDetailEntities);
            detailEntities = new ArrayList<>(detailEntities.stream().collect(Collectors.toMap(PlatformCouponSettlementDetailDO::getId, value -> value, (existingValue, newValue) -> newValue)).values());
            entity.setDetails(detailEntities);
            // 更新统计数
            entity.setTotalCount((long)detailEntities.size());// 明细总数
            Double amount = detailEntities.stream().map(PlatformCouponSettlementDetailDO::getSettlementAmount).map(BigDecimal::valueOf).reduce(BigDecimal::add).map(BigDecimal::doubleValue).orElse(0.0D);
            entity.setAmount(amount);// 结算金额
            // 更新
            repository.saveAndFlush(entity);
            // 删除多余的平台优惠券结算
            Long id = entity.getId();
            List<PlatformCouponSettlementDO> removeEntities = newEntities.stream().filter(f -> !Objects.equals(f.getId(), id)).collect(Collectors.toList());
            repository.deleteAll(removeEntities);
            // 新增系统消息通知
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(entity.getMemberId());
            request.setRoleId(entity.getRoleId());
            request.setMessageNotice(MessageTemplateCode.integral_check);
            List<String> params = new ArrayList<>();
            params.add(entity.getSettlementNo());
            request.setParams(params);
            feignService.sendSystemMessage(request);
        } else {
            if (entity.getId() != null && entity.getId() > 0
                    && entity.getTotalCount() <= 0) {
                repository.delete(entity);
            }
            logger.info("统计平台结算-优惠券:结算策略[{}-{}-结算会员:{}-结算角色:{}],无需结算", strategy.getId(), strategy.getName(), memberId, roleId);
        }
        return Wrapper.success();
    }

    /**
     * 根据会员id、角色id、结算结束时间、结束开始时间、平台优化策略、结算时间 获取平台优惠券结束数据
     *
     * @param memberId 会员id
     * @param roleId 角色id
     * @param endTime 结算结束时间
     * @param startTime 结算开始时间
     * @param strategy 平台结算策略
     * @param settlementDate 结算日期
     * @return 平台优惠券结束数据
     */
    private List<PlatformCouponSettlementDO> findByMemberIdAndRoleIdAndEndTimeAndStartTime(Long memberId, Long roleId,  long endTime,long startTime, PlatformSettlementStrategyDO strategy, long settlementDate) {
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("memberId").as(Long.class), memberId);
            Predicate field2 = builder.equal(root.get("roleId").as(Long.class), roleId);
            Predicate field3 = builder.equal(root.get("startTime").as(Long.class), 0L);
            Predicate field4 = builder.equal(root.get("endTime").as(Long.class), 0L);
            Predicate field5 = builder.between(root.get("createTime").as(Long.class), startTime, endTime);
            Predicate and1 = builder.and(field3, field4);
            Predicate or = builder.or(and1, field5);
            return query.where(field1, field2, or).getRestriction();
        };
        SettlementWayEnum settlementWay = SettlementWayEnum.getEnum(strategy.getSettlementWay());
        Integer settlementDays = strategy.getSettlementDays();
        return repository.findAll(spec).stream().filter(f -> {
            LocalDateTime createDateTime = OrderDateTimeUtil.timestampToLocalDateTime(f.getCreateTime());
            if (settlementWay == SettlementWayEnum.ACCOUNT_PERIOD) {
                LocalDateTime settlementDateTime = createDateTime.plusDays(settlementDays).withHour(1).withMinute(0).withSecond(0);
                // 结算日期
                long targetSettlementDate = settlementDateTime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                return targetSettlementDate == settlementDate;
            } else if (settlementWay == SettlementWayEnum.MONTH_SETTLEMENT) {
                LocalDateTime settlementDateTime = createDateTime.plusMonths(1).withHour(1).withMinute(0).withSecond(0);
                // 结算日期
                long targetSettlementDate = settlementDateTime.toLocalDate().atStartOfDay().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                return targetSettlementDate == settlementDate;
            } else {
                return false;
            }
        }).collect(Collectors.toList());
    }

    /**
     * 根据会员id、角色id获取平台优惠券按支付时间（创建时间）降序的第一条数据
     * @param memberId 会员id
     * @param roleId 角色id
     * @return 最后一次优惠券结算
     */
    private PlatformCouponSettlementDetailDO getDetailByMemberIdAndRoleIdOrderByPayTimeDescFirst(Long memberId, Long roleId) {
        Specification<PlatformCouponSettlementDetailDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("memberId").as(Long.class), memberId);
            Predicate field2 = builder.equal(root.get("roleId").as(Long.class), roleId);
            Order orderFiled1 = builder.desc(root.get("payTime").as(Long.class));
            return query.where(field1, field2).orderBy(orderFiled1).getRestriction();
        };
        return detailRepository.findAll(spec).stream().findFirst().orElse(null);
    }

    /**
     * 根据会员id、角色id获取 平台优惠价结算
     * @param memberId 会员id
     * @param roleId 角色id
     * @return 平台优惠价结算
     */
    private List<PlatformCouponSettlementDO> findPendingSettlementByMemberIdAndRoleId(Long memberId, Long roleId) {
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("settlementDate").as(Long.class), 0L);
            Predicate field2 = builder.equal(root.get("memberId").as(Long.class), memberId);
            Predicate field3 = builder.equal(root.get("roleId").as(Long.class), roleId);
            return query.where(field1, field2, field3).getRestriction();
        };
        return repository.findAll(spec);
    }

    /**
     * 根据结算日期、会员id、角色id、结算方式获取 平台优惠券结算
     * @param settlementDate 结算日期
     * @param memberId 会员id
     * @param roleId 角色id
     * @param settlementWay 结算方式
     * @return 平台优惠券结算
     */
    private PlatformCouponSettlementDO getBySettlementDateAndMemberIdAndRoleIdAndSettlementWay(Long settlementDate, Long memberId, Long roleId, Integer settlementWay) {
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("settlementDate").as(Long.class), settlementDate);
            Predicate field2 = builder.equal(root.get("memberId").as(Long.class), memberId);
            Predicate field3 = builder.equal(root.get("roleId").as(Long.class), roleId);
            Predicate field4 = builder.equal(root.get("settlementWay").as(Integer.class), settlementWay);
            return query.where(field1,field2,field3,field4).getRestriction();
        };
        return repository.findAll(spec).stream().findFirst().orElse(null);
    }

    /**
     * 获取策略默认会员集合
     * @param strategy 策略
     * @return 会员集合
     */
    private List<PlatformSettlementStrategyMemberDO> findStrategyDefaultMemberList(PlatformSettlementStrategyDO strategy) {
        // step1: 策略指定会员
        Boolean errorFlag = Optional.ofNullable(strategy).map(PlatformSettlementStrategyDO::getMemberList).map(CollectionUtils::isEmpty).orElse(true);
        if (errorFlag) {
            logger.info("统计平台结算-积分:结算策略[{}-{}]未有下属会员", Optional.ofNullable(strategy).map(PlatformSettlementStrategyDO::getId).orElse(0L), Optional.ofNullable(strategy).map(PlatformSettlementStrategyDO::getName).orElse("未知"));
            new ArrayList<>();
        }
        return Optional.ofNullable(strategy).map(PlatformSettlementStrategyDO::getMemberList).orElse(new ArrayList<>());
    }

    /**
     * 获取平台有效结算策略
     * @param dateTime 当前时间
     * @return 获取平台有效结算策略
     */
    private List<PlatformSettlementStrategyDO> findPlatformSettlementStrategyList(LocalDateTime dateTime) {
        // 获取当前时间对应的天
        int day = dateTime.getDayOfMonth();
        Specification<PlatformSettlementStrategyDO> strategySpec = (root, query, builder) -> {
            Predicate field1 = builder.equal(root.get("status").as(Integer.class), SettlementStrategyStatusEnum.VALID.getCode());
            Predicate field2 = builder.equal(root.get("settlementOrderType").as(Integer.class), PlatformSettlementOrderTypeEnum.COUPON.getCode());
            Predicate field3 = builder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.ACCOUNT_PERIOD.getCode());
            Predicate field4 = builder.and(builder.equal(root.get("settlementWay").as(Integer.class), SettlementWayEnum.MONTH_SETTLEMENT.getCode()),
                    builder.equal(root.get("settlementDate").as(Integer.class), day));
            Predicate or = builder.or(field3, field4);
            return query.where(field1, field2, or).getRestriction();
        };
        return platformSettlementStrategyRepository.findAll(strategySpec);
    }

    @Transactional
    @Override
    public Wrapper<Void> manualSettlement(UserLoginCacheDTO sysUser, Long id) {
        // step1: 查询需要手动结算的平台优惠券结算数据
        PlatformCouponSettlementDO entity = repository.findById(id).orElse(null);
        // step2: 数据校验
        if (Objects.isNull(entity)) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        List<PlatformCouponSettlementDO> entities = findByIdGreaterThanEqualAndMemberIdAndRoleIdOrderBySettlementDateAsc(id, entity);
        if (CollectionUtils.isEmpty(entities)) {
            throw new BusinessException(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        // 如果结算数据有已对账数据，则不允许重跑结算
        if (entities.stream().noneMatch(f -> Objects.equals(f.getStatus(), SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode()))) {
            throw new BusinessException(ResponseCode.SA_SETTLEMENT_DATA_CHECK);
        }
        // step3: 执行结算
        for (PlatformCouponSettlementDO one:
        entities) {
            PlatformSettlementStrategyDO strategy = platformSettlementStrategyRepository.findById(one.getStrategyId()).orElse(null);
            LocalDateTime settlementDate = OrderDateTimeUtil.timestampToLocalDateTime(one.getSettlementDate());
            // 如果对应的结算策略已删除或无效,则删除当前结算数据
            if (Objects.isNull(strategy) || Objects.equals(SettlementStrategyStatusEnum.INVALID.getCode(), strategy.getStatus())) {
                // 删除优惠券结算数据
                List<PlatformCouponSettlementDetailDO> detailEntities = one.getDetails();
                repository.deleteById(one.getId());
                // 删除优惠券结算明细数据
                detailRepository.deleteAll(detailEntities);
            }
            // 如果策略设置默认所有会员，直接重新结算
            else if (Objects.equals(CommonBooleanEnum.YES.getCode(), strategy.getIsDefault())) {
                platformCouponSettlementCommon(strategy,settlementDate, one.getMemberId(), one.getRoleId());
            }
            // 如果策略设置指定会员，结算会员在策略中,直接重新结算
            else if (!CollectionUtils.isEmpty(strategy.getMemberList())
                    && strategy.getMemberList().stream().anyMatch(f -> Objects.equals(f.getMemberId(), one.getMemberId()) || Objects.equals(f.getRoleId(), one.getMemberId()))) {
                platformCouponSettlementCommon(strategy,settlementDate, one.getMemberId(), one.getRoleId());
            } else {
                // 删除优惠券结算数据
                List<PlatformCouponSettlementDetailDO> detailEntities = one.getDetails();
                // 删除优惠券结算明细数据
                detailRepository.deleteAll(detailEntities);
                repository.deleteById(one.getId());
            }

        }
        return Wrapper.success();
    }

    /**
     * 获取会员id、角色id、id大于等于，结算时间升序的平台优惠券结算
     * @param id id
     * @param entity 会员id、角色id
     * @return 平台优惠券结算
     */
    private List<PlatformCouponSettlementDO> findByIdGreaterThanEqualAndMemberIdAndRoleIdOrderBySettlementDateAsc(Long id, PlatformCouponSettlementDO entity) {
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            Predicate field1 = builder.ge(root.get("id").as(Long.class), id);
            Predicate field2 = builder.equal(root.get("memberId").as(Long.class), entity.getMemberId());
            Predicate field3 = builder.equal(root.get("roleId").as(Long.class), entity.getRoleId());
            Order orderField = builder.asc(root.get("settlementDate").as(Long.class));
            return query.where(field1, field2, field3).orderBy(orderField).getRestriction();
        };
        return repository.findAll(spec);
    }

    @Override
    public Wrapper<PageData<PlatformCouponSettlementResponseVO>> pagePayableSettlement(UserLoginCacheDTO sysUser, PlatformCouponSettlementQueryRequestVO queryParameter) {
        return pageSettlement(sysUser, queryParameter, true);
    }

    @Override
    public Wrapper<PageData<PlatformCouponSettlementResponseVO>> pageReceivableSettlement(UserLoginCacheDTO sysUser, PlatformCouponSettlementQueryRequestVO queryParameter) {
        return pageSettlement(sysUser, queryParameter, false);
    }

    /**
     * 分页查询平台优惠券结算
     * @param sysUser 登录用户
     * @param queryParameter 查询条件
     * @param isSettlement 是否结算
     * @return 平台优惠券结算
     */
    private Wrapper<PageData<PlatformCouponSettlementResponseVO>> pageSettlement(UserLoginCacheDTO sysUser, PlatformCouponSettlementQueryRequestVO queryParameter, boolean isSettlement) {
        // step1: 组装查询条件
        Specification<PlatformCouponSettlementDO> spec = (root, query, builder) -> {
            List<Predicate> fieldList = new ArrayList<>();
            fieldList.add(builder.isNotNull(root.get("status").as(Integer.class)));
            if (!Objects.equals(SettlementStatusEnum.ALL.getCode(), queryParameter.getStatus())) {
                fieldList.add(builder.equal(root.get("status").as(Integer.class), queryParameter.getStatus()));
            }
            if (StringUtils.hasLength(queryParameter.getStartTime())) {
                fieldList.add(builder.greaterThanOrEqualTo(root.get("settlementDate").as(Long.class), DateUtil.parse(queryParameter.getStartTime()).getTime()));
            }
            if (StringUtils.hasLength(queryParameter.getEndTime())) {
                fieldList.add(builder.lessThanOrEqualTo(root.get("settlementDate").as(Long.class), DateUtil.parse(queryParameter.getEndTime()).getTime()));
            }
            if (isSettlement) {
                if (StringUtils.hasLength(queryParameter.getSettlementName())) {
                    fieldList.add(builder.like(root.get("settlementName").as(String.class), "%" + queryParameter.getSettlementName().trim() + "%"));
                }
            } else {
                fieldList.add(builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()));
                fieldList.add(builder.equal(root.get("roleId").as(Long.class), sysUser.getMemberRoleId()));
            }
            if (StringUtils.hasLength(queryParameter.getSettlementNo())) {
                fieldList.add(builder.like(root.get("settlementNo").as(String.class), "%" + queryParameter.getSettlementNo().trim() + "%"));
            }
            return query.where(fieldList.toArray(new Predicate[0])).getRestriction();
        };
        // step 2: 组装分页参数
        Pageable page = PageRequest.of(queryParameter.getCurrent() - 1, queryParameter.getPageSize(), Sort.by("id").descending());
        // step 3: 分页查询数据
        Page<PlatformCouponSettlementDO> result = repository.findAll(spec, page);
        // step 4: 组装返回数据
        List<PlatformCouponSettlementResponseVO> resultList = result.getContent().stream().map(this::buildPlatformCouponSettlementResponseVO).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    @Override
    public Wrapper<PlatformCouponSettlementResponseVO> getInfo(Long id) {
        PlatformCouponSettlementDO entity = repository.findById(id).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        PlatformCouponSettlementResponseVO result = buildPlatformCouponSettlementResponseVO(entity);
        return Wrapper.success(result);
    }

    private PlatformCouponSettlementResponseVO buildPlatformCouponSettlementResponseVO(PlatformCouponSettlementDO entity) {
        PlatformCouponSettlementResponseVO result = new PlatformCouponSettlementResponseVO();
        result.setId(entity.getId());
        result.setMemberId(entity.getMemberId());
        result.setRoleId(entity.getRoleId());
        result.setSettlementName(entity.getSettlementName());
        result.setSettlementNo(entity.getSettlementNo());
        result.setSettlementDate(Optional.ofNullable(entity.getSettlementDate()).map(map -> DateUtil.format(DateUtil.date(map), "yyyy-MM-dd")).orElse("") );
        result.setEstimatedPaymentDate(Optional.ofNullable(entity.getEstimatedPaymentDate()).map(map -> DateUtil.format(DateUtil.date(map), "yyyy-MM-dd")).orElse(""));
        result.setSettlementWay(entity.getSettlementWay());
        result.setSettlementWayName(SettlementWayEnum.getMessage(entity.getSettlementWay()));
        result.setSettlementTime(Optional.ofNullable(entity.getSettlementTime()).map(map -> DateUtil.format(DateUtil.date(map), "yyyy-MM-dd HH:mm")).orElse(""));
        result.setStatus(entity.getStatus());
        result.setStatusName(SettlementStatusEnum.getMessage(entity.getStatus()));
        result.setPayWay(entity.getPayWay());
        result.setPayWayName(SettlementTypeEnum.getName(entity.getPayWay()));
        result.setTotalCount(entity.getTotalCount());
        // 预计付款日期
        result.setEstimatedPaymentDate(Optional.ofNullable(entity.getEstimatedPaymentDate()).filter(f -> f.compareTo(0L)>0).map( map -> DateUtil.format(DateUtil.date(map), "yyyy-MM-dd")).orElse(""));
        // 时间付款日期
        result.setActualPaymentTime(Optional.ofNullable(entity.getActualPaymentTime()).filter(f -> f.compareTo(0L) >0).map(map -> DateUtil.format(DateUtil.date(map), "yyyy-MM-dd HH:mm")).orElse("") );
        result.setAmount(BigDecimal.valueOf(entity.getAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        return result;
    }

    @Override
    public Wrapper<PageData<PlatformCouponSettlementDetailResponseVO>> pagePayableSettlementDetail(UserLoginCacheDTO sysUser, PlatformCouponSettlementDetailQueryRequestVO queryParameter) {
        return pageSettlementDetail(sysUser, queryParameter, true);
    }

    @Override
    public Wrapper<PageData<PlatformCouponSettlementDetailResponseVO>> pageReceivableSettlementDetail(UserLoginCacheDTO sysUser, PlatformCouponSettlementDetailQueryRequestVO queryParameter) {
        return pageSettlementDetail(sysUser, queryParameter, false);
    }

    /**
     * 分页查询结算明细
     * @param sysUser 登录用户
     * @param queryParameter 查询条件
     * @param isSettlement 是否结算
     * @return 分页查询结算明细
     */
    private Wrapper<PageData<PlatformCouponSettlementDetailResponseVO>> pageSettlementDetail(UserLoginCacheDTO sysUser, PlatformCouponSettlementDetailQueryRequestVO queryParameter, boolean isSettlement) {
        // step 1: 组装查询条件
        Specification<PlatformCouponSettlementDetailDO> spec = buildDetailSpecification(sysUser, queryParameter, isSettlement);
        // step 2: 组装分页参数
        Pageable page = PageRequest.of(queryParameter.getCurrent() - 1, queryParameter.getPageSize(), Sort.by("orderTime").descending());
        // step 3: 分页查询数据
        Page<PlatformCouponSettlementDetailDO> result = detailRepository.findAll(spec, page);
        // step 4: 组装返回数据
        List<PlatformCouponSettlementDetailResponseVO> resultList = result.getContent().stream().map(this::buildPlatformCouponSettlementDetailResponseVO).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(result.getTotalElements(), resultList));
    }

    /**
     * 构建查询条件
     * @param sysUser 登录用户
     * @param queryParameter 查询条件
     * @param isSettlement 是否结算
     * @return 查询条件
     */
    private Specification<PlatformCouponSettlementDetailDO> buildDetailSpecification(UserLoginCacheDTO sysUser, PlatformCouponSettlementDetailQueryRequestVO queryParameter, boolean isSettlement) {
        return (root, query, builder) -> {
            PlatformCouponSettlementDO entity = new PlatformCouponSettlementDO();
            List<Predicate> fieldList = new ArrayList<>();
            entity.setId(queryParameter.getSettlementId());
            fieldList.add(builder.equal(root.get("settlement").as(PlatformCouponSettlementDO.class), entity));
            if (!isSettlement) {
                fieldList.add(builder.equal(root.get("memberId").as(Long.class), sysUser.getMemberId()));
            }
            if (StringUtils.hasLength(queryParameter.getOrderStartTime())) {
                fieldList.add(builder.greaterThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(queryParameter.getOrderStartTime()).getTime()));
            }
            if (StringUtils.hasLength(queryParameter.getOrderEndTime())) {
                fieldList.add(builder.lessThanOrEqualTo(root.get("orderTime").as(Long.class), DateUtil.parse(queryParameter.getOrderEndTime()).getTime()));
            }
            if (StringUtils.hasLength(queryParameter.getPayStartTime())) {
                fieldList.add(builder.greaterThanOrEqualTo(root.get("payTime").as(Long.class), DateUtil.parse(queryParameter.getPayStartTime()).getTime()));
            }
            if (StringUtils.hasLength(queryParameter.getPayEndTime())) {

                fieldList.add(builder.lessThanOrEqualTo(root.get("payTime").as(Long.class), DateUtil.parse(queryParameter.getPayEndTime().concat(" 23:59:59")).getTime()));
            }
            if (StringUtils.hasLength(queryParameter.getOrderNo())) {
                fieldList.add(builder.like(root.get("orderNo").as(String.class), "%" + queryParameter.getOrderNo().trim() + "%"));
            }
            if (StringUtils.hasLength(queryParameter.getOrderAbstract())) {
                fieldList.add(builder.like(root.get("orderAbstract").as(String.class), "%" + queryParameter.getOrderAbstract().trim() + "%"));
            }
            return query.where(fieldList.toArray(new Predicate[0])).getRestriction();
        };
    }

    /**
     * 构建返回平台优惠券结算数据
     * @param detailEntity 平台结算明细数据
     * @return 返回平台优惠券结算数据
     */
    private PlatformCouponSettlementDetailResponseVO buildPlatformCouponSettlementDetailResponseVO(PlatformCouponSettlementDetailDO detailEntity) {
        PlatformCouponSettlementDetailResponseVO result = new PlatformCouponSettlementDetailResponseVO();
        result.setId(detailEntity.getId());
        result.setOrderNo(detailEntity.getOrderNo());
        result.setOrderAbstract(detailEntity.getOrderAbstract());
        result.setSettlementOrderType(detailEntity.getSettlementOrderType());
        result.setSettlementOrderTypeName(SettlementOrderTypeEnum.getMessage(detailEntity.getSettlementOrderType(),""));
        result.setReturnCode(detailEntity.getReturnCode());
        result.setOrderTime(DateUtil.format(DateUtil.date(detailEntity.getOrderTime()), "yyyy-MM-dd"));
        result.setOrderType(detailEntity.getOrderType());
        result.setOrderTypeName(OrderTypeEnum.getNameByCode(detailEntity.getOrderType()));
        result.setOrderAmount(BigDecimal.valueOf(detailEntity.getOrderAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        result.setPayTime(DateUtil.format(DateUtil.date(detailEntity.getPayTime()), "yyyy-MM-dd HH:mm"));
        result.setCouponId(detailEntity.getCouponId());
        result.setCouponNo(detailEntity.getCouponNo());
        result.setCouponAmount(BigDecimal.valueOf(detailEntity.getCouponAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        result.setSettlementAmount(BigDecimal.valueOf(detailEntity.getSettlementAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
        return result;
    }

    @Override
    public void payableExport(UserLoginCacheDTO sysUser,HttpServletResponse response, PlatformCouponSettlementDetailExcelQueryRequestVO queryParameter) {
        PlatformCouponSettlementDetailQueryRequestVO query = buildPlatformCouponSettlementDetailQueryRequestVO(queryParameter);
        exportCommon(sysUser, response, query, true);
    }

    /**
     * 通用导出功能
     * @param sysUser 登录用户
     * @param response 响应
     * @param queryParameter 查询条件
     * @param isSettlement 是否结算
     */
    private void exportCommon(UserLoginCacheDTO sysUser, HttpServletResponse response, PlatformCouponSettlementDetailQueryRequestVO queryParameter, boolean isSettlement) {
        // step 1: 组装查询条件
        Specification<PlatformCouponSettlementDetailDO> spec = buildDetailSpecification(sysUser, queryParameter, isSettlement);
        // step 2: 获取结算明细数据
        List<PlatformCouponSettlementDetailDO> resultList = detailRepository.findAll(spec);
        // step 3: 设置导出模板数据
        List<Map<String, Object>> rows = resultList.stream().map(item -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put(PlatformCouponSettlementExcelEnum.ORDER_NO.getMessage(), item.getOrderNo());
            map.put(PlatformCouponSettlementExcelEnum.ORDER_ABSTRACT.getMessage(), item.getOrderAbstract());
            String returnCode = Optional.ofNullable(item.getReturnCode()).orElse("");
            map.put(PlatformCouponSettlementExcelEnum.SETTLEMENT_ORDER_TYPE.getMessage(), SettlementOrderTypeEnum.getMessage(item.getSettlementOrderType(), returnCode) + returnCode);
            map.put(PlatformCouponSettlementExcelEnum.ORDER_TIME.getMessage(), OrderDateTimeUtil.timestampToDateTimeString(item.getOrderTime()));
            map.put(PlatformCouponSettlementExcelEnum.ORDER_TYPE.getMessage(), OrderTypeEnum.getNameByCode(item.getOrderType()));
            map.put(PlatformCouponSettlementExcelEnum.ORDER_AMOUNT.getMessage(), BigDecimal.valueOf(item.getOrderAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            map.put(PlatformCouponSettlementExcelEnum.COUPON_AMOUNT.getMessage(), BigDecimal.valueOf(item.getCouponAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            map.put(PlatformCouponSettlementExcelEnum.COUPON_NO.getMessage(), item.getCouponNo());
            map.put(PlatformCouponSettlementExcelEnum.PAY_TIME.getMessage(), OrderDateTimeUtil.timestampToDateTimeString(item.getPayTime()));
            map.put(PlatformCouponSettlementExcelEnum.SETTLEMENT_AMOUNT.getMessage(), BigDecimal.valueOf(item.getSettlementAmount()).setScale(2, RoundingMode.HALF_UP).doubleValue());
            return map;
        }).collect(Collectors.toList());
        // 获取excel写入器，默认写出到第一个sheet
        ExcelWriter writer = ExcelUtil.getBigWriter();
        // 获取头部样式，获取样式后可自定义样式
        CellStyle style = writer.getHeadCellStyle();
        // 设置文本是否应换行。 将此标志设置为true通过在多行上显示单元格中的所有内容来使所有内容可见
        style.setWrapText(false);
        // 写入数据
        writer.write(rows);
        String fileName =PlatformCouponSettlementExcelEnum.FILE_NAME.getMessage() + "-" + DateUtil.date() + ".xls";
        // 写入响应
        try (ServletOutputStream out = response.getOutputStream()) {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            writer.flush(out, true);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    @Override
    public void  receivableExport(UserLoginCacheDTO sysUser,HttpServletResponse response, PlatformCouponSettlementDetailExcelQueryRequestVO queryParameter) {
        PlatformCouponSettlementDetailQueryRequestVO query = buildPlatformCouponSettlementDetailQueryRequestVO(queryParameter);
        exportCommon(sysUser, response, query, false);
    }

    private PlatformCouponSettlementDetailQueryRequestVO buildPlatformCouponSettlementDetailQueryRequestVO(PlatformCouponSettlementDetailExcelQueryRequestVO queryParameter) {
        PlatformCouponSettlementDetailQueryRequestVO query = new PlatformCouponSettlementDetailQueryRequestVO();
        query.setSettlementId(queryParameter.getSettlementId());
        query.setOrderNo(queryParameter.getOrderNo());
        query.setOrderAbstract(queryParameter.getOrderAbstract());
        query.setOrderStartTime(queryParameter.getOrderStartTime());
        query.setOrderEndTime(queryParameter.getOrderEndTime());
        query.setPayStartTime(queryParameter.getPayStartTime());
        query.setPayEndTime(queryParameter.getPayEndTime());
        return query;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> confirmAccountComplete(UserLoginCacheDTO sysUser, ConfirmAccountCompleteVO completeVO) {
        PlatformCouponSettlementDO entity = repository.findById(completeVO.getSettlementId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        if (!Objects.equals(SettlementStatusEnum.TO_BE_ACCOUNT_COMPLETE.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_SCORE_SETTLEMENT_NO_CONFIRM_ACCOUNT_COMPLETE);
        }
        entity.setStatus(SettlementStatusEnum.TO_BE_PAY.getCode());
        entity.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(entity);

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待对账-1，待付款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(0L);
        noticeVO.setRoleId(0L);
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_ACCOUNTS.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待对账-1，待付款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_ACCOUNTS.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        List<SystemMessageRequest> requestList = new ArrayList<>();
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(0L);
        request.setRoleId(0L);
        request.setMessageNotice(MessageTemplateCode.integral_upload_bill);
        request.setParams(params);
        requestList.add(request);
        request = new SystemMessageRequest();
        request.setMemberId(entity.getMemberId());
        request.setRoleId(entity.getRoleId());
        request.setMessageNotice(MessageTemplateCode.integral_upload_bill);
        request.setParams(params);
        requestList.add(request);
        feignService.batchSendSystemMessage(requestList);
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> confirmPayProve(UserLoginCacheDTO sysUser, ConfirmPayProve confirmVO) {
        PlatformCouponSettlementDO entity = repository.findById(confirmVO.getId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        if (!Objects.equals(SettlementStatusEnum.TO_BE_RECEIPT.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NO_CONFIRM_PAY_PROVE);
        }
        if (confirmVO.getStatus().equals(CommonBooleanEnum.YES.getCode())) {
            entity.setStatus(SettlementStatusEnum.COMPLETE.getCode());
        } else {
            entity.setStatus(SettlementStatusEnum.TO_BE_PAY.getCode());
        }
        entity.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(entity);

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待收款-1，已完成/待付款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(0L);
        noticeVO.setRoleId(0L);
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setReduceCount(1L);
        if (SettlementStatusEnum.COMPLETE.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.COMPLETE.getCode());
        } else if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        }
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待收款-1，已完成/待付款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setReduceCount(1L);
        if (SettlementStatusEnum.COMPLETE.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.COMPLETE.getCode());
        } else if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        }
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        if (SettlementStatusEnum.TO_BE_PAY.getCode().equals(entity.getStatus())) {
            // 新增系统通知
            List<SystemMessageRequest> requestList = new ArrayList<>();
            List<String> params = new ArrayList<>();
            params.add(entity.getSettlementNo());
            SystemMessageRequest request = new SystemMessageRequest();
            request.setMemberId(0L);
            request.setRoleId(0L);
            request.setMessageNotice(MessageTemplateCode.integral_upload_bill);
            request.setParams(params);
            requestList.add(request);
            request = new SystemMessageRequest();
            request.setMemberId(entity.getMemberId());
            request.setRoleId(entity.getRoleId());
            request.setMessageNotice(MessageTemplateCode.integral_upload_bill);
            request.setParams(params);
            requestList.add(request);
            feignService.batchSendSystemMessage(requestList);
        }
        return Wrapper.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> pay(UserLoginCacheDTO sysUser, PayVO payVO) {
        PlatformCouponSettlementDO entity = repository.findById(payVO.getId()).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        if (!Objects.equals(SettlementStatusEnum.TO_BE_PAY.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NO_PAY);
        }
        entity.setPayProveList(payVO.getProveList());
        entity.setStatus(SettlementStatusEnum.TO_BE_RECEIPT.getCode());
        entity.setActualPaymentTime(System.currentTimeMillis());
        entity.setUpdateTime(System.currentTimeMillis());
        repository.saveAndFlush(entity);

        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待付款-1，待收款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(0L);
        noticeVO.setRoleId(0L);
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待付款-1，待收款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        List<SystemMessageRequest> requestList = new ArrayList<>();
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(0L);
        request.setRoleId(0L);
        request.setMessageNotice(MessageTemplateCode.integral_gathering);
        request.setParams(params);
        requestList.add(request);
        request = new SystemMessageRequest();
        request.setMemberId(entity.getMemberId());
        request.setRoleId(entity.getRoleId());
        request.setMessageNotice(MessageTemplateCode.integral_gathering);
        request.setParams(params);
        requestList.add(request);
        feignService.batchSendSystemMessage(requestList);
        return Wrapper.success();
    }

    @Override
    public Wrapper<List<PayProveBO>> findPayProves(Long id) {
        PlatformCouponSettlementDO entity = repository.findById(id).orElse(null);
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        return Wrapper.success(entity.getPayProveList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> allInPay(UserLoginCacheDTO sysUser, AllInPayVO payVO) {
        // step1: 查询平台积分结算
        PlatformCouponSettlementDO entity = repository.findById(payVO.getId()).orElse(null);
        // step2: 校验
        if (Objects.isNull(entity)) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NOT_EXIST);
        }
        if (!Objects.equals(SettlementStatusEnum.TO_BE_PAY.getCode(), entity.getStatus())) {
            return Wrapper.fail(ResponseCode.SA_PLATFORM_COUPON_SETTLEMENT_NO_PAY);
        }
        // step3: 更新状态已完成
        entity.setStatus(SettlementStatusEnum.COMPLETE.getCode());
        entity.setActualPaymentTime(System.currentTimeMillis());
        entity.setUpdateTime(System.currentTimeMillis());
        // 生成支付流水号
        String tradeNo = entity.getSettlementNo() + "-" + RandomStringUtils.random(7, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
        entity.setTradeNo(tradeNo);
        repository.saveAndFlush(entity);
        // step4: 通联支付
        PlatFormTransferRequest allInPayRequest = new PlatFormTransferRequest();
        allInPayRequest.setMemberId(entity.getMemberId()); // 会员id(收款方)
        allInPayRequest.setMemberRoleId(entity.getRoleId()); // 会员角色id(收款方)
        allInPayRequest.setOrderCode(tradeNo); // 商户订单号
        allInPayRequest.setPayMoney(entity.getAmount()); // 金额
        Wrapper<ApplicationTransferRespVO> payWrapper = feignService.platFormTransfer(allInPayRequest);
        if (payWrapper == null) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(ResponseCode.SERVICE_PAY_ERROR);
        }
        if (payWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Wrapper.fail(payWrapper.getCode(), payWrapper.getMessage());
        }
        // 新增操作通知
        List<OperateNoticeVO> noticeVOList = new ArrayList<>();
        // 支付方待付款-1，待收款+1
        OperateNoticeVO noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(0L);
        noticeVO.setRoleId(0L);
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        // 收款方待付款-1，待收款+1
        noticeVO = new OperateNoticeVO();
        noticeVO.setMemberId(entity.getMemberId());
        noticeVO.setRoleId(entity.getRoleId());
        noticeVO.setDataSource(OperateDataSourceEnum.SETTLE_ACCOUNT_PLATFORM_COUPON.getCode());
        noticeVO.setReduceOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_PAY.getCode());
        noticeVO.setReduceCount(1L);
        noticeVO.setIncreaseOperateType(SettleAccountPlatformScoreOperateTypeEnum.TO_BE_RECEIPT.getCode());
        noticeVO.setIncreaseCount(1L);
        noticeVOList.add(noticeVO);
        feignService.addSystemOperateNoticeList(noticeVOList);

        // 新增系统通知
        List<SystemMessageRequest> requestList = new ArrayList<>();
        List<String> params = new ArrayList<>();
        params.add(entity.getSettlementNo());
        SystemMessageRequest request = new SystemMessageRequest();
        request.setMemberId(0L);
        request.setRoleId(0L);
        request.setMessageNotice(MessageTemplateCode.integral_gathering);
        request.setParams(params);
        requestList.add(request);
        request = new SystemMessageRequest();
        request.setMemberId(entity.getMemberId());
        request.setRoleId(entity.getRoleId());
        request.setMessageNotice(MessageTemplateCode.integral_gathering);
        request.setParams(params);
        requestList.add(request);
        feignService.batchSendSystemMessage(requestList);
        return Wrapper.success();
    }
}
